package com.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JwtToken生成的工具类
 * JWT token的格式：header.payload.signature
 * header的格式（算法、token的类型）：
 * {"alg": "HS512","type": "JWT"}
 * payload的格式（用户名、创建时间、过期时间）：
 * {"sub":"wang","created":1489079981393,"exp":1489684781}
 * signature的生成算法：
 * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
 */

/**
 * 1 、生成token 交给前端  generateToken（String username）
 * 2 、验证token 是否合法  getClaimsFromToken（String token） 返回null表示不合法，重新登录
 *                        validateToken(String token,String username) 验收token 是否合法， 用户名不正确，或者已过期
 * 3 、验证token 是否过期  isTokenExpired（String token)    返回true表示过期，重新登录
 * 4 、生成新token 交给前端  refreshHeadToken（String token） 为了更新过期时间，
 */
@Slf4j
@Component//将类交给Spring容器管理
public class JWTUtil {

    private static final String CLAIM_KEY_USERNAME = "sub";//sub 就是subject的缩写  jwt所面向的用户
    private static final String CLAIM_KEY_CREATED = "iat";//jwt 的签发时间


    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private Long expiration;
    @Value("${jwt.tokenHead}")
    private String tokenHead;
    @Value("${jwt.refresh}")
    private Long refresh;


    private  Claims claims;

    //claims  包含创建时间
    //token   包含claims 和过期时间 半个小时后过期

    /*public void init(String secret, Long expiration, String tokenHead,Long refresh) {
        this.secret = secret;
        this.expiration = expiration;
        this.tokenHead = tokenHead;
        this.refresh=refresh;
    }*/

    /**
     * 根据用户信息生成token
     */
    public String generateToken(String username) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, username);
        Date create = new Date();
        log.info("创建时间为："+create+"  "+TimeUtil.getFormatDate(create));

        claims.put(CLAIM_KEY_CREATED, create.getTime() / 1000);//设置为秒级
        return Jwts.builder()//没用设置头，但是生成依然是三部分
                .setClaims(claims)//设置payload 负载  claims有自己的方法，也是一个map
                .setExpiration(new Date(create.getTime()+ expiration * 1000))//设置过期时间 // 设置过期时间，保持毫秒级
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    //获取 subject
    public String getClaimKeyUsername(String token){
        claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        return claims.getSubject();
    }

    /**
     * 验证token是否还有效  用户名存在，并且token没有过期就是有效的
     *
     * @param token       客户端传入的token
     * @param username 从数据库中查询出来的用户信息
     */
    public String validateToken(String token, String username) {
        if(token.equals("")||token==null){
            log.error("token校验错误，token为空");
            return "token为空";
        }
        if(username.equals("")||username==null){
            log.error("token校验错误，username为空");
            return "token为空";
        }
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();//过期显示了报错
        }catch (ExpiredJwtException e){
            return "token过期";
        }

        if(claims==null){
            return "token无效";
        }
        String tokenUsername = claims.getSubject();
        if(tokenUsername==null){
            return "token无效";
        }
        if(!tokenUsername.equals(username)){
            return "token无效";
        }
        Date expiredDate = claims.getExpiration();//获得过期日期
        boolean flag=expiredDate.before(new Date());//过期日期在当前日期之前，就是过期
        if(flag){
            return "token失效";
        }else{
            return "token有效";
        }
    }

    //或者校验token成功后，或者调用接口结束后     如果需要都可以重新设置token， 只要最后一秒通过接口校验，就能更新token
    private boolean needRefreshToken(String token){
        claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        if(claims==null){
            log.error("判断是否需要更新，此处token无效");
            return false;
        }
        Date exDate = claims.getExpiration();
        //log.info("过期时间为："+TimeUtil.getFormatDate(exDate));
        Date createDate=claims.getIssuedAt();//获得创建时间  秒级的问题
        //当前时间在刷新时间之后，已经校验通过之后判断是否需要更新
        //log.info("创建时间为："+TimeUtil.getFormatDate(createDate));
        Date refeshDate =new Date(createDate.getTime()+refresh*1000);
        //log.info("刷新时间为："+TimeUtil.getFormatDate(refeshDate));
        return new Date().after(refeshDate);
    }

    public String refreshToken(String token){
        if(needRefreshToken(token)){
            String username=claims.getSubject();//获取用户信息
            return generateToken(username);
        }else{
            return null;
        }
    }
}

